#ifndef EASY_CAR_UI_RENDER_ENGINE_VULKAN_ENV_H_
#define EASY_CAR_UI_RENDER_ENGINE_VULKAN_ENV_H_


#include <stdexcept>
#include <vector>
#include <map>
#include <set>


#include "Model/Model.h"
#include "Renderer/Vulkan/PhysicalDevice.h"
#include "Renderer/Vulkan/Pipeline.h"
#include "Renderer/Vulkan/Texture.h"
#include "Renderer/Vulkan/VulkanNode.h"

namespace namespace_easy_car_ui
{;

class Camera
{
public:
    glm::vec3 m_posistion {};
    glm::vec3 m_face {};
    glm::vec3 m_up {};
    float m_yaw{-90};
    float m_pitch{0};
    float m_deltaYaw{0};
    float m_deltaPitch{0};
    float m_speed {0.05};
    bool m_isForward {false};
    bool m_isRetreated {false};
    bool m_isRightwards {false};
    bool m_isLeftwards {false};

    Camera();
    ~Camera();
    void UpdatePositon(float deltaSeconds);
    void UpdateFace(float deltaYaw, float deltaPitch);
    void CommitFace(float deltaYaw, float deltaPitch);
};

class VulkanRenderer
{
private:
    VulkanRenderer();
public:
    ~VulkanRenderer();
    static VulkanRenderer& GetInstance();
    VulkanRenderer(const VulkanRenderer&) = delete;
    VulkanRenderer(const VulkanRenderer&&) = delete;
    VulkanRenderer& operator=(const VulkanRenderer&) = delete;
    VulkanRenderer& operator=(const VulkanRenderer&&) = delete;

    void Initialize(const bool enableDebug, uint32_t width, uint32_t height);
    void Deinitialize();
    void RenderOneFrame(uint32_t* pixels, const float seconds);
    void AddModel(const Model::Ptr& model);

    void PrintPhysicalDevices();
    void PrintSelectedPhysicalDevice();

private:
    void GetAllSupportedLayers();
    void CreateVkInstance();
    void SetDebugUtilMessenger();
    void EnumeratePhysicalDevices();
    void SeletPhysicalDevice();
    void CreateDevice();
    void CreateRenderPass();
    void CreateFramebuffers();
    void CreateCommandPool();
    void CreateCommandBuffer();
    void CreateSyncObjects();
    void CreatePresentBuffer();
    void RecordCommandBuffer();
    void RecordOnVkNodeCommandBuffer(VulkanNode::Ptr vkNode);
    void CreateVkNodeByPart(const Part::Ptr);
    void CreateAndMapUniformBuffer();
    void UpdateUniformBuffer(float deltaSeconds);
    void CreateDescriptorPool();
    void CreateDescriptorSets();
    void CreateDepthResources();
    VkFormat FindDepthFormat();
    VkFormat FindSupportedFormat(const std::vector<VkFormat>& candidates, VkImageTiling tiling, VkFormatFeatureFlags features);

public:
    Camera m_camera {};

private:
    bool m_enableDebug{false};
    uint32_t m_width {0};
    uint32_t m_height {0};
    uint64_t m_imageSize {0};
    std::set<std::string> m_supportedLayerNames{};
    VkInstance m_vkInstance {};
    VkDebugUtilsMessengerEXT m_debugUtilsMessenger {};
    VkDebugUtilsMessengerCreateInfoEXT m_debugUtilsMessengerCreateInfo {};
    std::vector<PhysicalDevice::Ptr> m_physicalDevices {};
    PhysicalDevice::Ptr m_selectedPhysicalDevice {nullptr};
    VkDevice m_device {};
    VkQueue m_graphicsQueue {};
    VkRenderPass m_renderPass {};
    VkImage m_colorImage{};
    VkImageView m_colorImageView{};
    VkDeviceMemory m_colorImageMemory{};
    VkFramebuffer m_framebuffer{};
    Pipeline m_pipeline{};
    VkCommandPool m_commandPool{};
    VkCommandBuffer m_commandBuffer{};
    VkFence m_fence {};
    VkBuffer m_presentBuffer{};
    VkDeviceMemory m_presentImageMemory{};
    std::vector<VulkanNode::Ptr> m_vkNodes{};
    std::vector<Model::Ptr> m_models{};
    glm::mat4 view{};
    glm::mat4 proj{};
    VkBuffer m_uniformBuffer {};
    VkDeviceMemory m_uniformBuffersMemory {};
    void* m_uniformBufferMapped {nullptr};
    VkDescriptorPool m_descriptorPool{};
    VkDescriptorSet m_descriptorSet{};
    Texture::Ptr m_texture{};
    VkImage m_depthImage {};
    VkDeviceMemory m_depthImageMemory {};
    VkImageView m_depthImageView {};

    
    
    
    

};


} /* namespace namespace_easy_car_ui */

#endif /*EASY_CAR_UI_RENDER_ENGINE_VULKAN_ENV_H_*/

